/**************************************************************************
    Lightspark, a free flash player implementation

    Copyright (C) 2012-2013  Alessandro Pignotti (a.pignotti@sssup.it)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

#include "tiny_string.h"
#include "exceptions.h"
#include "swf.h"

using namespace lightspark;

tiny_string::tiny_string(std::istream& in, int len):buf(_buf_static),stringSize(len+1),type(STATIC)
{
	if(stringSize > STATIC_SIZE)
		createBuffer(stringSize);
	in.read(buf,len);
	buf[len]='\0';
	init();
}

tiny_string::tiny_string(const char* s,bool copy):_buf_static(),buf(_buf_static),type(READONLY)
{
	if(copy)
		makePrivateCopy(s);
	else
	{
		stringSize=strlen(s)+1;
		buf=(char*)s; //This is an unsafe conversion, we have to take care of the RO data
	}
	init();
}

tiny_string::tiny_string(const tiny_string& r):
	_buf_static(),buf(_buf_static),stringSize(r.stringSize),numchars(r.numchars),type(STATIC),isASCII(r.isASCII),hasNull(r.hasNull)
{
	//Fast path for static read-only strings
	if(r.type==READONLY)
	{
		type=READONLY;
		buf=r.buf;
		return;
	}
	if(stringSize > STATIC_SIZE)
		createBuffer(stringSize);
	memcpy(buf,r.buf,stringSize);
}

tiny_string::tiny_string(const std::string& r):_buf_static(),buf(_buf_static),stringSize(r.size()+1),type(STATIC)
{
	if(stringSize > STATIC_SIZE)
		createBuffer(stringSize);
	memcpy(buf,r.c_str(),stringSize);
	init();
}

tiny_string::~tiny_string()
{
	resetToStatic();
}

tiny_string& tiny_string::operator=(const tiny_string& s)
{
	resetToStatic();
	stringSize=s.stringSize;
	//Fast path for static read-only strings
	if(s.type==READONLY)
	{
		type=READONLY;
		buf=s.buf;
	}
	else
	{
		if(stringSize > STATIC_SIZE)
			createBuffer(stringSize);
		memcpy(buf,s.buf,stringSize);
	}
	this->isASCII = s.isASCII;
	this->hasNull = s.hasNull;
	this->numchars = s.numchars;
	return *this;
}

tiny_string& tiny_string::operator=(const std::string& s)
{
	resetToStatic();
	stringSize=s.size()+1;
	if(stringSize > STATIC_SIZE)
		createBuffer(stringSize);
	memcpy(buf,s.c_str(),stringSize);
	init();
	return *this;
}

tiny_string& tiny_string::operator=(const char* s)
{
	makePrivateCopy(s);
	init();
	return *this;
}

tiny_string& tiny_string::operator+=(const char* s)
{	//deprecated, cannot handle '\0' inside string
	if(type==READONLY)
	{
		char* tmp=buf;
		makePrivateCopy(tmp);
	}
	uint32_t addedLen=strlen(s);
	uint32_t newStringSize=stringSize + addedLen;
	if(type==STATIC && newStringSize > STATIC_SIZE)
	{
		createBuffer(newStringSize);
		//don't copy trailing \0
		memcpy(buf,_buf_static,stringSize-1);
	}
	else if(type==DYNAMIC && addedLen!=0)
		resizeBuffer(newStringSize);
	//also copy \0 at the end
	memcpy(buf+stringSize-1,s,addedLen+1);
	stringSize=newStringSize;
	init();
	return *this;
}

tiny_string& tiny_string::operator+=(const tiny_string& r)
{
	if(type==READONLY)
	{
		char* tmp=buf;
		makePrivateCopy(tmp);
	}
	uint32_t newStringSize=stringSize + r.stringSize-1;
	if(type==STATIC && newStringSize > STATIC_SIZE)
	{
		createBuffer(newStringSize);
		//don't copy trailing \0
		memcpy(buf,_buf_static,stringSize-1);
	}
	else if(type==DYNAMIC && r.stringSize>1)
		resizeBuffer(newStringSize);
	//start position is where the \0 was
	memcpy(buf+stringSize-1,r.buf,r.stringSize);
	stringSize=newStringSize;
	if (this->isASCII)
		this->isASCII = r.isASCII;
	if (!this->hasNull)
		this->hasNull = r.hasNull;
	this->numchars += r.numchars;
	return *this;
}

tiny_string& tiny_string::operator+=(const std::string& s)
{
	//TODO: optimize
	return *this += tiny_string(s);
}

tiny_string& tiny_string::operator+=(uint32_t c)
{
	return (*this += tiny_string::fromChar(c));
}

const tiny_string tiny_string::operator+(const tiny_string& r) const
{
	tiny_string ret(*this);
	ret+=r;
	return ret;
}

const tiny_string tiny_string::operator+(const char* s) const
{
	return *this + tiny_string(s);
}

const tiny_string tiny_string::operator+(const std::string& r) const
{
	return *this + tiny_string(r);
}

bool tiny_string::operator<(const tiny_string& r) const
{
	//don't check trailing \0
	int ret = memcmp(buf,r.buf,std::min(stringSize,r.stringSize));
	if (ret == 0)
		return stringSize < r.stringSize;
	return ret < 0;
}

bool tiny_string::operator>(const tiny_string& r) const
{
	//don't check trailing \0
	int ret = memcmp(buf,r.buf,std::min(stringSize,r.stringSize));
	if (ret == 0)
		return stringSize > r.stringSize;
	return ret > 0;
}

bool tiny_string::operator==(const tiny_string& r) const
{
	//The length is checked as an optimization before checking the contents
	if(stringSize != r.stringSize)
		return false;
	//don't check trailing \0
	return memcmp(buf,r.buf,stringSize-1)==0;
}

bool tiny_string::operator==(const std::string& r) const
{
	//The length is checked as an optimization before checking the contents
	if(stringSize != r.size()+1)
		return false;
	//don't check trailing \0
	return memcmp(buf,r.c_str(),stringSize-1)==0;
}

bool tiny_string::operator!=(const std::string& r) const
{
	if(stringSize != r.size()+1)
		return true;
	//don't check trailing \0
	return memcmp(buf,r.c_str(),stringSize-1)!=0;
}

bool tiny_string::operator!=(const tiny_string& r) const
{
	return !(*this==r);
}

bool tiny_string::operator==(const char* r) const
{
	if(!r) return false;
	unsigned len = strlen(r);
	if(len!=stringSize-1) return false;
	return memcmp(buf,r,len)==0;
}

bool tiny_string::operator==(const xmlChar* r) const
{
	return *this==reinterpret_cast<const char*>(r);
}

bool tiny_string::operator!=(const char* r) const
{
	return !(*this==r);
}

char* tiny_string::strchr(char c) const
{
	//TODO: does this handle '\0' in middle of buf gracefully?
	return g_utf8_strchr(buf, numBytes(), c);
}

char* tiny_string::strchrr(char c) const
{
	//TODO: does this handle '\0' in middle of buf gracefully?
	return g_utf8_strrchr(buf, numBytes(), c);
}

tiny_string::operator std::string() const
{
	return std::string(buf,stringSize-1);
}

bool tiny_string::startsWith(const char* o) const
{
	return strncmp(buf,o,strlen(o)) == 0;
}
bool tiny_string::endsWith(const char* o) const
{
	size_t olen = strlen(o);
	return (numBytes() >= olen) && 
		(strncmp(buf+numBytes()-olen,o,olen) == 0);
}

/* start is an index of characters.
 * returns index of character */
uint32_t tiny_string::find(const tiny_string& needle, uint32_t start) const
{
	if (isASCII && !hasNull)
	{
		// fast path for ascii strings
		const char* p = strstr(buf+start,needle.raw_buf());
		return (p ? p-buf : npos);
	}
	gchar* gp = g_utf8_offset_to_pointer(buf,start);
	gchar* found =g_strstr_len(gp,-1,needle.raw_buf());
	if(found == nullptr)
		return npos;
	else
		return start + g_utf8_pointer_to_offset(gp,found);
}
bool tiny_string::getLine(uint32_t& byteindex, tiny_string& line)
{
	bool res = false;
	unsigned char utfpos=0;
	uint32_t startindex = byteindex;
	uint32_t endindex = stringSize-byteindex-1;
	if (endindex < startindex)
		endindex = stringSize-endindex;
	line.isASCII = true;
	while (!res && byteindex < stringSize-1)
	{
		switch((uint8_t)buf[byteindex])
		{
			case 0x00:
				line.hasNull=true;
				break;
			case '\n':
			case '\r':
				res=true;
				endindex=byteindex;
				break;
			case 0xe2:
				// utf-8 line separators:
				// e2 80 a8 (unicode 0x2028)
				// e2 80 a9 (unicode 0x2029)
				if (byteindex < stringSize-3)
				{
					if ((uint8_t)buf[byteindex+1] == 0x80 && ((uint8_t)buf[byteindex+2] == 0xa8 || (uint8_t)buf[byteindex+2] == 0xa9))
					{
						endindex=byteindex;
						byteindex+=2;
						res=true;
						break;
					}
				}
				break;
		}
		if (!res)
		{
			if (buf[byteindex] & 0x80)
			{
				if (utfpos == 0)
				{
					utfpos = buf[byteindex];
				}
				utfpos = utfpos << 1;
				if (!(utfpos & 0x80))
				{
					line.numchars++;
					utfpos = 0;
				}
				line.isASCII = false;
			}
			else
				line.numchars++;
		}
		byteindex++;
	}
	//prepare line for new size
	uint32_t newStringSize=endindex-startindex+1;
	if(line.type==READONLY)
		line.resetToStatic();
	if(line.type==STATIC && newStringSize > STATIC_SIZE)
		line.createBuffer(newStringSize);
	else if(line.type==DYNAMIC && newStringSize > line.stringSize)
		line.resizeBuffer(newStringSize);

	// copy part from startindex to byteindex into line
	memcpy(line.buf,buf+startindex,newStringSize-1);
	line.buf[newStringSize-1]=0x00;
	line.stringSize=newStringSize;
	if (byteindex>=stringSize-1)
		byteindex = tiny_string::npos;
	return res;
}

uint32_t tiny_string::rfind(const tiny_string& needle, uint32_t start) const
{
	//TODO: omit copy into std::string
	size_t bytestart;
	if(start == npos)
		bytestart = std::string::npos;
	else
		bytestart = g_utf8_offset_to_pointer(buf,start) - buf;

	size_t bytepos = std::string(*this).rfind(needle.raw_buf(),bytestart,needle.numBytes());
	if(bytepos == std::string::npos)
		return npos;
	else
		return g_utf8_pointer_to_offset(buf,buf+bytepos);
}

void tiny_string::makePrivateCopy(const char* s)
{
	resetToStatic();
	stringSize=strlen(s)+1;
	if(stringSize > STATIC_SIZE)
		createBuffer(stringSize);
	strcpy(buf,s);
}

void tiny_string::createBuffer(uint32_t s)
{
	type=DYNAMIC;
	reportMemoryChange(s);
	buf=new char[s];
}

void tiny_string::resizeBuffer(uint32_t s)
{
	assert(type==DYNAMIC);
	char* oldBuf=buf;
	reportMemoryChange(s-stringSize);
	buf=new char[s];
	assert(s >= stringSize);
	memcpy(buf,oldBuf,stringSize);
	delete[] oldBuf;
}

void tiny_string::resetToStatic()
{
	if(type==DYNAMIC)
	{
		reportMemoryChange(-stringSize);
		delete[] buf;
	}
	stringSize=1;
	_buf_static[0] = '\0';
	buf=_buf_static;
	type=STATIC;
}

void tiny_string::init()
{
	numchars = 0;
	isASCII = true;
	hasNull = false;
	unsigned char utfpos=0;
	for (unsigned int i = 0; i < stringSize-1; i++)
	{
		if (buf[i] & 0x80)
		{
			if (utfpos == 0)
			{
				utfpos = buf[i];
			}
			utfpos = utfpos << 1;
			if (!(utfpos & 0x80))
			{
				numchars++;
				utfpos = 0;
			}
			isASCII = false;
		}
		else
			numchars++;
		if (buf[i] == 0)
			hasNull = true;
	}
}

tiny_string tiny_string::fromChar(uint32_t c)
{
	tiny_string ret;
	ret.buf = ret._buf_static;
	ret.type = STATIC;
	ret.isASCII = c<0x80;
	if (ret.isASCII)
	{
		ret.buf[0] = c&0xff;
		ret.stringSize = 2;
	}
	else
	{
		ret.stringSize =  g_unichar_to_utf8(c,ret.buf) + 1;
	}
	ret.buf[ret.stringSize-1] = '\0';
	ret.hasNull = c == 0;
	ret.numchars = 1;
	return ret;
}

tiny_string& tiny_string::replace(uint32_t pos1, uint32_t n1, const tiny_string& o )
{
	assert(pos1 <= numChars());
	if(pos1 + n1 > numChars())
		n1 = numChars()-pos1;
	if (isASCII)
		return replace_bytes(pos1, n1, o);
	uint32_t bytestart = g_utf8_offset_to_pointer(buf,pos1)-buf;
	uint32_t byteend = g_utf8_offset_to_pointer(buf,pos1+n1)-buf;
	return replace_bytes(bytestart, byteend-bytestart, o);
}

tiny_string& tiny_string::replace_bytes(uint32_t bytestart, uint32_t bytenum, const tiny_string& o)
{
	uint32_t newlen = this->stringSize+o.numBytes()-bytenum;
	assert(bytestart+bytenum<stringSize);
	char* newbuf = new char[newlen];
	memcpy(newbuf,this->raw_buf(),bytestart);
	memcpy(newbuf+bytestart,o.raw_buf(),o.numBytes());
	memcpy(newbuf+bytestart+o.numBytes(),this->raw_buf()+bytestart+bytenum,this->stringSize-(bytestart+bytenum));
	newbuf[newlen-1] = '\0';
	if(type==DYNAMIC)
	{
		reportMemoryChange(-stringSize);
		delete[] buf;
	}
	this->type=DYNAMIC;
	this->buf=newbuf;
	this->stringSize=newlen;
	if (this->isASCII && o.isASCII)
	{
		this->numchars = newlen-1;
		this->hasNull |= o.hasNull;
	}
	else
		this->init();
	return *this;
}

tiny_string tiny_string::substr_bytes(uint32_t start, uint32_t len, bool resultisascii) const
{
	tiny_string ret;
	if (start >= stringSize)
		return ret;
	if ((len == UINT32_MAX) || (start+len >= stringSize))
		len =stringSize-(start+1);
	assert(start+len < stringSize);
	if(len+1 > STATIC_SIZE)
		ret.createBuffer(len+1);
	memcpy(ret.buf,buf+start,len);
	ret.buf[len]=0;
	ret.stringSize = len+1;
	if (this->isASCII && !this->hasNull)
		ret.numchars = len;
	else if (resultisascii)
	{
		ret.numchars = len;
		ret.hasNull=false;
	}
	else
		ret.init();
	return ret;
}

tiny_string tiny_string::substr(uint32_t start, uint32_t len) const
{
	assert_and_throw(start <= numChars());
	if(len > numChars()-start)
		len = numChars()-start;
	if (isASCII)
		return substr_bytes(start, len);
	uint32_t bytestart = g_utf8_offset_to_pointer(buf,start) - buf;
	uint32_t byteend = g_utf8_offset_to_pointer(buf+bytestart,len) - (buf+bytestart);
	return substr_bytes(bytestart, byteend,byteend-bytestart == len && !this->hasNull);
}

tiny_string tiny_string::substr(uint32_t start, const CharIterator& end) const
{
	if (isASCII)
		return substr_bytes(start, (end.buf_ptr - buf)-start);
	assert_and_throw(start < numChars());
	uint32_t bytestart = g_utf8_offset_to_pointer(buf,start) - buf;
	uint32_t byteend = end.buf_ptr - buf;
	return substr_bytes(bytestart, byteend-bytestart);
}

std::list<tiny_string> tiny_string::split(uint32_t delimiter) const
{
	std::list<tiny_string> res;
	uint32_t pos, end;
	tiny_string delimiterstring = tiny_string::fromChar(delimiter);

	pos = 0;
	unsigned int len = numChars();
	while (pos < len)
	{
		end = find(delimiterstring, pos);
		if (end == tiny_string::npos)
		{
			res.push_back(substr(pos, len-pos));
			break;
		}
		else
		{
			res.push_back(substr(pos, end-pos));
			pos = end+1;
		}
	}
	
	return res;
}

// upper/lowercase handling taken form avmplus

const uint16_t lowerCaseBase[] =
{
	/*0x0061,   0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A,
	0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
	0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A,*/   0x00B5, /*0x00E0,   0x00E1, 0x00E2,
	0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC,
	0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6,
	0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE,*/   0x00FF, /*0x0101,   0x0103,
	0x0105, 0x0107, 0x0109, 0x010B, 0x010D, 0x010F, 0x0111, 0x0113, 0x0115, 0x0117,
	0x0119, 0x011B, 0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127, 0x0129, 0x012B,
	0x012D, 0x012F,*/   0x0131, 0x0133, 0x0135, 0x0137, /*0x013A,   0x013C, 0x013E, 0x0140,
	0x0142, 0x0144, 0x0146, 0x0148,*/   /*0x014B,   0x014D, 0x014F, 0x0151, 0x0153, 0x0155,
	0x0157, 0x0159, 0x015B, 0x015D, 0x015F, 0x0161, 0x0163, 0x0165, 0x0167, 0x0169,
	0x016B, 0x016D, 0x016F, 0x0171, 0x0173, 0x0175, 0x0177,*/   0x017A, 0x017C, 0x017E,
	0x017F, 0x0183, 0x0185, 0x0188, 0x018C, 0x0192, 0x0195, 0x0199, 0x01A1, 0x01A3,
	0x01A5, 0x01A8, 0x01AD, 0x01B0, 0x01B4, 0x01B6, 0x01B9, 0x01BD, 0x01BF, 0x01C5,
	0x01C6, 0x01C8, 0x01C9, 0x01CB, 0x01CC, 0x01CE, 0x01D0, 0x01D2, 0x01D4, 0x01D6,
	0x01D8, 0x01DA, 0x01DC, 0x01DD, 0x01DF, 0x01E1, 0x01E3, 0x01E5, 0x01E7, 0x01E9,
	0x01EB, 0x01ED, 0x01EF, 0x01F2, 0x01F3, 0x01F5, 0x01F9, 0x01FB, 0x01FD, 0x01FF,
	/*0x0201,   0x0203, 0x0205, 0x0207, 0x0209, 0x020B, 0x020D, 0x020F, 0x0211, 0x0213,
	0x0215, 0x0217, 0x0219, 0x021B, 0x021D, 0x021F, 0x0223, 0x0225, 0x0227, 0x0229,
	0x022B, 0x022D, 0x022F, 0x0231, 0x0233,*/   0x0253, 0x0254, 0x0256, 0x0257, 0x0259,
	0x025B, 0x0260, 0x0263, 0x0268, 0x0269, 0x026F, 0x0272, 0x0275, 0x0280, 0x0283,
	0x0288, 0x028A, 0x028B, 0x0292, 0x0345, 0x03AC, 0x03AD, 0x03AE, 0x03AF, /*0x03B1,
	0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
	0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5,
	0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB,*/   0x03CC, 0x03CD, 0x03CE, 0x03D0,
	0x03D1, 0x03D5, 0x03D6, /*0x03DB,   0x03DD, 0x03DF, 0x03E1, 0x03E3, 0x03E5, 0x03E7,
	0x03E9, 0x03EB, 0x03ED, 0x03EF,*/   0x03F0, 0x03F1, 0x03F2, 0x03F5, /*0x0430,   0x0431,
	0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B,
	0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445,
	0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,*/
	/*0x0450,   0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459,
	0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,*/   /*0x0461,   0x0463, 0x0465, 0x0467,
	0x0469, 0x046B, 0x046D, 0x046F, 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047B,
	0x047D, 0x047F, 0x0481, 0x048D, 0x048F, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499,
	0x049B, 0x049D, 0x049F, 0x04A1, 0x04A3, 0x04A5, 0x04A7, 0x04A9, 0x04AB, 0x04AD,
	0x04AF, 0x04B1, 0x04B3, 0x04B5, 0x04B7, 0x04B9, 0x04BB, 0x04BD, 0x04BF,*/   0x04C2,
	0x04C4, 0x04C8, 0x04CC, /*0x04D1,   0x04D3, 0x04D5, 0x04D7, 0x04D9, 0x04DB, 0x04DD,
	0x04DF, 0x04E1, 0x04E3, 0x04E5, 0x04E7, 0x04E9, 0x04EB, 0x04ED, 0x04EF, 0x04F1,
	0x04F3, 0x04F5, 0x04F9, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
	0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, 0x0570, 0x0571,
	0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B,
	0x057C, 0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585,
	0x0586, 0x1E01, 0x1E03, 0x1E05, 0x1E07, 0x1E09, 0x1E0B, 0x1E0D, 0x1E0F, 0x1E11,
	0x1E13, 0x1E15, 0x1E17, 0x1E19, 0x1E1B, 0x1E1D, 0x1E1F, 0x1E21, 0x1E23, 0x1E25,
	0x1E27, 0x1E29, 0x1E2B, 0x1E2D, 0x1E2F, 0x1E31, 0x1E33, 0x1E35, 0x1E37, 0x1E39,
	0x1E3B, 0x1E3D, 0x1E3F, 0x1E41, 0x1E43, 0x1E45, 0x1E47, 0x1E49, 0x1E4B, 0x1E4D,
	0x1E4F, 0x1E51, 0x1E53, 0x1E55, 0x1E57, 0x1E59, 0x1E5B, 0x1E5D, 0x1E5F, 0x1E61,
	0x1E63, 0x1E65, 0x1E67, 0x1E69, 0x1E6B, 0x1E6D, 0x1E6F, 0x1E71, 0x1E73, 0x1E75,
	0x1E77, 0x1E79, 0x1E7B, 0x1E7D, 0x1E7F, 0x1E81, 0x1E83, 0x1E85, 0x1E87, 0x1E89,
	0x1E8B, 0x1E8D, 0x1E8F, 0x1E91, 0x1E93, 0x1E95,*/   0x1E9B, /*0x1EA1,   0x1EA3, 0x1EA5,
	0x1EA7, 0x1EA9, 0x1EAB, 0x1EAD, 0x1EAF, 0x1EB1, 0x1EB3, 0x1EB5, 0x1EB7, 0x1EB9,
	0x1EBB, 0x1EBD, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1EC9, 0x1ECB, 0x1ECD,
	0x1ECF, 0x1ED1, 0x1ED3, 0x1ED5, 0x1ED7, 0x1ED9, 0x1EDB, 0x1EDD, 0x1EDF, 0x1EE1,
	0x1EE3, 0x1EE5, 0x1EE7, 0x1EE9, 0x1EEB, 0x1EED, 0x1EEF, 0x1EF1, 0x1EF3, 0x1EF5,
	0x1EF7, 0x1EF9,*/   /*0x1F00,   0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,*/
	/*0x1F10,   0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F20, 0x1F21, 0x1F22, 0x1F23,
	0x1F24, 0x1F25, 0x1F26, 0x1F27, 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35,
	0x1F36, 0x1F37,*/   0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F51, 0x1F53,
	0x1F55, 0x1F57, 0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,
	0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1F76, 0x1F77, 0x1F78, 0x1F79,
	0x1F7A, 0x1F7B, 0x1F7C, 0x1F7D, 0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85,
	0x1F86, 0x1F87, 0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,
	0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7, 0x1FB0, 0x1FB1,
	0x1FB3, 0x1FBE, 0x1FC3, 0x1FD0, 0x1FD1, 0x1FE0, 0x1FE1, 0x1FE5, 0x1FF3  /*0x2170,
	0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
	0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4,
	0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE,
	0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8,
	0x24E9  0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49,
	0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53,
	0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A*/
};

const uint16_t upperCaseConversion[] =
{
	/*0x0041,   0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A,
	0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054,
	0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A,*/   0x039C, /*0x00C0,   0x00C1, 0x00C2,
	0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC,
	0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE,*/   0x0178, /*0x0100,   0x0102,
	0x0104, 0x0106, 0x0108, 0x010A, 0x010C, 0x010E, 0x0110, 0x0112, 0x0114, 0x0116,
	0x0118, 0x011A, 0x011C, 0x011E, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012A,
	0x012C, 0x012E,*/   0x0049, 0x0132, 0x0134, 0x0136, /*0x0139,   0x013B, 0x013D, 0x013F,
	0x0141, 0x0143, 0x0145, 0x0147,*/   /*0x014A,   0x014C, 0x014E, 0x0150, 0x0152, 0x0154,
	0x0156, 0x0158, 0x015A, 0x015C, 0x015E, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168,
	0x016A, 0x016C, 0x016E, 0x0170, 0x0172, 0x0174, 0x0176,*/   0x0179, 0x017B, 0x017D,
	0x0053, 0x0182, 0x0184, 0x0187, 0x018B, 0x0191, 0x01F6, 0x0198, 0x01A0, 0x01A2,
	0x01A4, 0x01A7, 0x01AC, 0x01AF, 0x01B3, 0x01B5, 0x01B8, 0x01BC, 0x01F7, 0x01C4,
	0x01C4, 0x01C7, 0x01C7, 0x01CA, 0x01CA, 0x01CD, 0x01CF, 0x01D1, 0x01D3, 0x01D5,
	0x01D7, 0x01D9, 0x01DB, 0x018E, 0x01DE, 0x01E0, 0x01E2, 0x01E4, 0x01E6, 0x01E8,
	0x01EA, 0x01EC, 0x01EE, 0x01F1, 0x01F1, 0x01F4, 0x01F8, 0x01FA, 0x01FC, 0x01FE,
	/*0x0200,   0x0202, 0x0204, 0x0206, 0x0208, 0x020A, 0x020C, 0x020E, 0x0210, 0x0212,
	0x0214, 0x0216, 0x0218, 0x021A, 0x021C, 0x021E, 0x0222, 0x0224, 0x0226, 0x0228,
	0x022A, 0x022C, 0x022E, 0x0230, 0x0232,*/   0x0181, 0x0186, 0x0189, 0x018A, 0x018F,
	0x0190, 0x0193, 0x0194, 0x0197, 0x0196, 0x019C, 0x019D, 0x019F, 0x01A6, 0x01A9,
	0x01AE, 0x01B1, 0x01B2, 0x01B7, 0x0399, 0x0386, 0x0388, 0x0389, 0x038A, /*0x0391,
	0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B,
	0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A3, 0x03A4, 0x03A5,
	0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB,*/   0x038C, 0x038E, 0x038F, 0x0392,
	0x0398, 0x03A6, 0x03A0, /*0x03DA,   0x03DC, 0x03DE, 0x03E0, 0x03E2, 0x03E4, 0x03E6,
	0x03E8, 0x03EA, 0x03EC, 0x03EE,*/   0x039A, 0x03A1, 0x03A3, 0x0395, /*0x0410,   0x0411,
	0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B,
	0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425,
	0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,*/
	/*0x0400,   0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
	0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F,*//*0x0460,  0x0462, 0x0464, 0x0466,
	0x0468, 0x046A, 0x046C, 0x046E, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047A,
	0x047C, 0x047E, 0x0480, 0x048C, 0x048E, 0x0490, 0x0492, 0x0494, 0x0496, 0x0498,
	0x049A, 0x049C, 0x049E, 0x04A0, 0x04A2, 0x04A4, 0x04A6, 0x04A8, 0x04AA, 0x04AC,
	0x04AE, 0x04B0, 0x04B2, 0x04B4, 0x04B6, 0x04B8, 0x04BA, 0x04BC, 0x04BE,*/   0x04C1,
	0x04C3, 0x04C7, 0x04CB, /*0x04D0,   0x04D2, 0x04D4, 0x04D6, 0x04D8, 0x04DA, 0x04DC,
	0x04DE, 0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA, 0x04EC, 0x04EE, 0x04F0,
	0x04F2, 0x04F4, 0x04F8, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
	0x0538, 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, 0x0540, 0x0541,
	0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, 0x0548, 0x0549, 0x054A, 0x054B,
	0x054C, 0x054D, 0x054E, 0x054F, 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555,
	0x0556, 0x1E00, 0x1E02, 0x1E04, 0x1E06, 0x1E08, 0x1E0A, 0x1E0C, 0x1E0E, 0x1E10,
	0x1E12, 0x1E14, 0x1E16, 0x1E18, 0x1E1A, 0x1E1C, 0x1E1E, 0x1E20, 0x1E22, 0x1E24,
	0x1E26, 0x1E28, 0x1E2A, 0x1E2C, 0x1E2E, 0x1E30, 0x1E32, 0x1E34, 0x1E36, 0x1E38,
	0x1E3A, 0x1E3C, 0x1E3E, 0x1E40, 0x1E42, 0x1E44, 0x1E46, 0x1E48, 0x1E4A, 0x1E4C,
	0x1E4E, 0x1E50, 0x1E52, 0x1E54, 0x1E56, 0x1E58, 0x1E5A, 0x1E5C, 0x1E5E, 0x1E60,
	0x1E62, 0x1E64, 0x1E66, 0x1E68, 0x1E6A, 0x1E6C, 0x1E6E, 0x1E70, 0x1E72, 0x1E74,
	0x1E76, 0x1E78, 0x1E7A, 0x1E7C, 0x1E7E, 0x1E80, 0x1E82, 0x1E84, 0x1E86, 0x1E88,
	0x1E8A, 0x1E8C, 0x1E8E, 0x1E90, 0x1E92, 0x1E94,*/   0x1E60, /*0x1EA0,   0x1EA2, 0x1EA4,
	0x1EA6, 0x1EA8, 0x1EAA, 0x1EAC, 0x1EAE, 0x1EB0, 0x1EB2, 0x1EB4, 0x1EB6, 0x1EB8,
	0x1EBA, 0x1EBC, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1EC8, 0x1ECA, 0x1ECC,
	0x1ECE, 0x1ED0, 0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EDA, 0x1EDC, 0x1EDE, 0x1EE0,
	0x1EE2, 0x1EE4, 0x1EE6, 0x1EE8, 0x1EEA, 0x1EEC, 0x1EEE, 0x1EF0, 0x1EF2, 0x1EF4,
	0x1EF6, 0x1EF8,*/   /*0x1F08,   0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F,*/
	/*0x1F18,   0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F28, 0x1F29, 0x1F2A, 0x1F2B,
	0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F, 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D,
	0x1F3E, 0x1F3F,*/   0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F59, 0x1F5B,
	0x1F5D, 0x1F5F, 0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F,
	0x1FBA, 0x1FBB, 0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FDA, 0x1FDB, 0x1FF8, 0x1FF9,
	0x1FEA, 0x1FEB, 0x1FFA, 0x1FFB, 0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D,
	0x1F8E, 0x1F8F, 0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F,
	0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF, 0x1FB8, 0x1FB9,
	0x1FBC, 0x0399, 0x1FCC, 0x1FD8, 0x1FD9, 0x1FE8, 0x1FE9, 0x1FEC, 0x1FFC  /*0x2160,
	0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A,
	0x216B, 0x216C, 0x216D, 0x216E, 0x216F  0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA,
	0x24BB, 0x24BC, 0x24BD, 0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4,
	0x24C5, 0x24C6, 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE,
	0x24CF  0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29,
	0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33,
	0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A*/
};


// 12sep02 grandma : table driven inline function is 14x faster than original function,
// Using first 100 movies of ATS, HashKey alone calls CharToUpper() 360,000 times.

// Only entries 0-254 are used in these tables in unicharToUpper and unicharToLower
// so entry 255 has been removed.  Entry 255 is handled by the lowerCaseBase code.

static const uint8_t tolower_map[] =
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //0-15
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //16-31
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //32-47
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //48-63
	0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //64-79
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, //80-95
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //96-111
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //112-127
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //128-143
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //144-159
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //160-175
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //176-191
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //192-207
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, //208-223
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //224-239
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00        //240-254
};

static const uint8_t toupper_map[] =
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //0-15
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //16-31
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //32-47
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //48-63
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //64-79
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //80-95
	0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //96-111
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, //112-127
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //128-143
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //144-159
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //160-175
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //176-191
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //192-207
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //208-223
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, //224-239
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20        //240-254
};


// WARNING: This is used by the core flash code. Any change to this utility, or the tables
//          it relies on, will break legacy Flash content.
uint32_t unicharToUpper(uint32_t ch)
{
	if (ch < 0xFF)
		return toupper_map[ch] ^ ch;

	// offset 0x1C60
	/*
	if ( (ch>=0x2D00 && ch<=0x2D25) )   // Georgian
	{
		return (ch - 0x1C60);
	}
	*/

	// offset 80
	if ( (ch>=0x0450 && ch<=0x045F) )
	{
		return (ch - 0x50);
	}

	// offset 48
	if ( (ch>=0x0561 && ch<=0x0586) )
	{
		return (ch - 0x30);
	}

	// offset -32
	if ( (ch>=0x03B1 && ch<=0x03CB) ||
		 (ch>=0x0430 && ch<=0x044F) ||
		 (ch>=0xFF41 && ch<=0xFF5A) )
	{
		return (ch - 0x20);
	}

	// offset 26
	if ( (ch>=0x24D0 && ch<=0x24E9) )
	{
		return (ch - 0x1A);
	}

	// offset 16
	if ( (ch>=0x2170 && ch<=0x217F) )
	{
		return (ch - 0x10);
	}

	// offset +8(positive)
	if ( (ch>=0x1F00 && ch<=0X1F07) ||
		 (ch>=0x1F10 && ch<=0x1F15) ||
		 (ch>=0x1F20 && ch<=0x1F27) ||
		 (ch>=0x1F30 && ch<=0x1F37) )
	{
		return (ch + 0x8);
	}

	// offset -1
	if ( (ch>=0x0101 && ch<=0x0233) )
	{
		if ( ((ch<=0x012F) && (ch&0x1)) ||                              // odd only
			 ((ch>=0x013A && ch<=0x0148) && !(ch&0x1)) ||               // even only
			 ((ch>=0x014B && ch<=0x0177) && (ch&1)) ||                  // odd only
			 ((ch>=0x0201 && ch<=0x0233) && (ch&1) && ch!=0x0221) )     // odd only
		{
			return (ch - 1);
		}
	}

	if ( ch&1 ) // only looking for odd chars here
	{
		// GREEK
		if ( ch>=0x03D9 && ch<=0x03EF )
				return (ch - 1);
		// CYRILLIC
		if ( ((ch>=0x0461 && ch<=0x04BF) && !(ch==0x0483 || ch==0x0485 || ch==0x487 || ch==0x0489)) ||
			  (ch>=0x04D1 && ch<=0x04F9) )
		{
				if (ch!=0x0483 && ch!=0x0485 && ch!=0x487 && ch!=0x0489)
					return (ch - 1);
		}
		// LATIN_EXT_ADD
		if ( (ch>=0x1E01 && ch<=0x1E95) || (ch>=0x1EA1 && ch<=0x1EF9) )
			return (ch - 1);
	}


	uint32_t result = ch;
	// Do a binary search in lowerCaseBase for char
	int32_t lo = 0;
	int32_t hi = (sizeof(lowerCaseBase) / sizeof(lowerCaseBase[0])) - 1;

	while (lo <= hi)
	{
		int32_t pivot = (lo+hi)>>1;
		uint32_t testChar = lowerCaseBase[pivot];

		if (ch == testChar)
		{
			// Use that index into lowerCaseConversion for a return value
			result = upperCaseConversion[pivot];
			break;
		}
		else if (ch < testChar)
		{
			hi = pivot-1;
		}
		else
		{
			lo = pivot+1;
		}
	}

	return result;
}

const uint16_t upperCaseBase[] =
{
	/*0x0100,   0x0102, 0x0104, 0x0106,
	0x0108, 0x010A, 0x010C, 0x010E, 0x0110, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A,
	0x011C, 0x011E, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012A, 0x012C, 0x012E,*/
	0x0130, 0x0132, 0x0134, 0x0136, /*0x0139,   0x013B, 0x013D, 0x013F, 0x0141, 0x0143,
	0x0145, 0x0147, 0x014A, 0x014C, 0x014E, 0x0150, 0x0152, 0x0154, 0x0156, 0x0158,
	0x015A, 0x015C, 0x015E, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016A, 0x016C,
	0x016E, 0x0170, 0x0172, 0x0174, 0x0176,*/   0x0178, 0x0179, 0x017B, 0x017D, 0x0181,
	0x0182, 0x0184, 0x0186, 0x0187, 0x0189, 0x018A, 0x018B, 0x018E, 0x018F, 0x0190,
	0x0191, 0x0193, 0x0194, 0x0196, 0x0197, 0x0198, 0x019C, 0x019D, 0x019F, 0x01A0,
	0x01A2, 0x01A4, 0x01A6, 0x01A7, 0x01A9, 0x01AC, 0x01AE, 0x01AF, 0x01B1, 0x01B2,
	0x01B3, 0x01B5, 0x01B7, 0x01B8, 0x01BC, 0x01C4, 0x01C5, 0x01C7, 0x01C8, 0x01CA,
	0x01CB, 0x01CD, 0x01CF, 0x01D1, 0x01D3, 0x01D5, 0x01D7, 0x01D9, 0x01DB, 0x01DE,
	0x01E0, 0x01E2, 0x01E4, 0x01E6, 0x01E8, 0x01EA, 0x01EC, 0x01EE, 0x01F1, 0x01F2,
	0x01F4, 0x01F6, 0x01F7, 0x01F8, 0x01FA, 0x01FC, 0x01FE, /*0x0200,   0x0202, 0x0204,
	0x0206, 0x0208, 0x020A, 0x020C, 0x020E, 0x0210, 0x0212, 0x0214, 0x0216, 0x0218,
	0x021A, 0x021C, 0x021E, 0x0222, 0x0224, 0x0226, 0x0228, 0x022A, 0x022C, 0x022E,
	0x0230, 0x0232,*/   0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 0x038F, /*0x0391,
	0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B,
	0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6,
	0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x03E2,
	0x03E4, 0x03E6, 0x03E8, 0x03EA, 0x03EC, 0x03EE,*/   0x03F4, /*0x0400,   0x0401, 0x0402,
	0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C,
	0x040D, 0x040E, 0x040F,*/   /*0x0410,   0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416,
	0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420,
	0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A,
	0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468,
	0x046A, 0x046C, 0x046E, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047A, 0x047C,
	0x047E, 0x0480, 0x048C, 0x048E, 0x0490, 0x0492, 0x0494, 0x0496, 0x0498, 0x049A,
	0x049C, 0x049E, 0x04A0, 0x04A2, 0x04A4, 0x04A6, 0x04A8, 0x04AA, 0x04AC, 0x04AE,
	0x04B0, 0x04B2, 0x04B4, 0x04B6, 0x04B8, 0x04BA, 0x04BC, 0x04BE,*/   0x04C1, 0x04C3,
	0x04C7, 0x04CB, /*0x04D0,   0x04D2, 0x04D4, 0x04D6, 0x04D8, 0x04DA, 0x04DC, 0x04DE,
	0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA, 0x04EC, 0x04EE, 0x04F0, 0x04F2,
	0x04F4, 0x04F8, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, 0x0538,
	0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F, 0x0540, 0x0541, 0x0542,
	0x0543, 0x0544, 0x0545, 0x0546, 0x0547, 0x0548, 0x0549, 0x054A, 0x054B, 0x054C,
	0x054D, 0x054E, 0x054F, 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556,*/
	/*
	// cn: added Georgian.  Not in the UnicodeData-3.2.0 spreadsheet, but was added later
	0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9,
	0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3,
	0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd,
	0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5,
	// cn: end Georgian.
	*/
	/*0x1E00,   0x1E02, 0x1E04, 0x1E06, 0x1E08, 0x1E0A, 0x1E0C, 0x1E0E, 0x1E10, 0x1E12,
	0x1E14, 0x1E16, 0x1E18, 0x1E1A, 0x1E1C, 0x1E1E, 0x1E20, 0x1E22, 0x1E24, 0x1E26,
	0x1E28, 0x1E2A, 0x1E2C, 0x1E2E, 0x1E30, 0x1E32, 0x1E34, 0x1E36, 0x1E38, 0x1E3A,
	0x1E3C, 0x1E3E, 0x1E40, 0x1E42, 0x1E44, 0x1E46, 0x1E48, 0x1E4A, 0x1E4C, 0x1E4E,
	0x1E50, 0x1E52, 0x1E54, 0x1E56, 0x1E58, 0x1E5A, 0x1E5C, 0x1E5E, 0x1E60, 0x1E62,
	0x1E64, 0x1E66, 0x1E68, 0x1E6A, 0x1E6C, 0x1E6E, 0x1E70, 0x1E72, 0x1E74, 0x1E76,
	0x1E78, 0x1E7A, 0x1E7C, 0x1E7E, 0x1E80, 0x1E82, 0x1E84, 0x1E86, 0x1E88, 0x1E8A,
	0x1E8C, 0x1E8E, 0x1E90, 0x1E92, 0x1E94, 0x1EA0, 0x1EA2, 0x1EA4, 0x1EA6, 0x1EA8,
	0x1EAA, 0x1EAC, 0x1EAE, 0x1EB0, 0x1EB2, 0x1EB4, 0x1EB6, 0x1EB8, 0x1EBA, 0x1EBC,
	0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1EC8, 0x1ECA, 0x1ECC, 0x1ECE, 0x1ED0,
	0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EDA, 0x1EDC, 0x1EDE, 0x1EE0, 0x1EE2, 0x1EE4,
	0x1EE6, 0x1EE8, 0x1EEA, 0x1EEC, 0x1EEE, 0x1EF0, 0x1EF2, 0x1EF4, 0x1EF6, 0x1EF8,
	0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F, 0x1F18, 0x1F19,
	0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D,
	0x1F2E, 0x1F2F, 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F,*/
	0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F59, 0x1F5B, 0x1F5D, 0x1F5F,
	0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F, 0x1F88, 0x1F89,
	0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F, 0x1F98, 0x1F99, 0x1F9A, 0x1F9B,
	0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F, 0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD,
	0x1FAE, 0x1FAF, 0x1FB8, 0x1FB9, 0x1FBA, 0x1FBB, 0x1FBC, 0x1FC8, 0x1FC9, 0x1FCA,
	0x1FCB, 0x1FCC, 0x1FD8, 0x1FD9, 0x1FDA, 0x1FDB, 0x1FE8, 0x1FE9, 0x1FEA, 0x1FEB,
	0x1FEC, 0x1FF8, 0x1FF9, 0x1FFA, 0x1FFB, 0x1FFC, 0x2126, 0x212A, 0x212B  /*0x2160,
	0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A,
	0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA,
	0x24BB, 0x24BC, 0x24BD, 0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4,
	0x24C5, 0x24C6, 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE,
	0x24CF, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29,
	0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33,
	0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A*/
};

const uint16_t lowerCaseConversion[] =
{
	/*0x0101,   0x0103, 0x0105, 0x0107,
	0x0109, 0x010B, 0x010D, 0x010F, 0x0111, 0x0113, 0x0115, 0x0117, 0x0119, 0x011B,
	0x011D, 0x011F, 0x0121, 0x0123, 0x0125, 0x0127, 0x0129, 0x012B, 0x012D, 0x012F,*/
	0x0069, 0x0133, 0x0135, 0x0137, /*0x013A,   0x013C, 0x013E, 0x0140, 0x0142, 0x0144,
	0x0146, 0x0148, 0x014B, 0x014D, 0x014F, 0x0151, 0x0153, 0x0155, 0x0157, 0x0159,
	0x015B, 0x015D, 0x015F, 0x0161, 0x0163, 0x0165, 0x0167, 0x0169, 0x016B, 0x016D,
	0x016F, 0x0171, 0x0173, 0x0175, 0x0177,*/   0x00FF, 0x017A, 0x017C, 0x017E, 0x0253,
	0x0183, 0x0185, 0x0254, 0x0188, 0x0256, 0x0257, 0x018C, 0x01DD, 0x0259, 0x025B,
	0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026F, 0x0272, 0x0275, 0x01A1,
	0x01A3, 0x01A5, 0x0280, 0x01A8, 0x0283, 0x01AD, 0x0288, 0x01B0, 0x028A, 0x028B,
	0x01B4, 0x01B6, 0x0292, 0x01B9, 0x01BD, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01CC,
	0x01CC, 0x01CE, 0x01D0, 0x01D2, 0x01D4, 0x01D6, 0x01D8, 0x01DA, 0x01DC, 0x01DF,
	0x01E1, 0x01E3, 0x01E5, 0x01E7, 0x01E9, 0x01EB, 0x01ED, 0x01EF, 0x01F3, 0x01F3,
	0x01F5, 0x0195, 0x01BF, 0x01F9, 0x01FB, 0x01FD, 0x01FF, /*0x0201,   0x0203, 0x0205,
	0x0207, 0x0209, 0x020B, 0x020D, 0x020F, 0x0211, 0x0213, 0x0215, 0x0217, 0x0219,
	0x021B, 0x021D, 0x021F, 0x0223, 0x0225, 0x0227, 0x0229, 0x022B, 0x022D, 0x022F,
	0x0231, 0x0233,*/   0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CC, 0x03CD, 0x03CE, /*0x03B1,
	0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB,
	0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6,
	0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03DB, 0x03DD, 0x03DF, 0x03E1, 0x03E3,
	0x03E5, 0x03E7, 0x03E9, 0x03EB, 0x03ED, 0x03EF,*/   0x03B8, /*0x0450,   0x0451, 0x0452,
	0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C,
	0x045D, 0x045E, 0x045F,*/   /*0x0430,   0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436,
	0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440,
	0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A,
	0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0461, 0x0463, 0x0465, 0x0467, 0x0469,
	0x046B, 0x046D, 0x046F, 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047B, 0x047D,
	0x047F, 0x0481, 0x048D, 0x048F, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499, 0x049B,
	0x049D, 0x049F, 0x04A1, 0x04A3, 0x04A5, 0x04A7, 0x04A9, 0x04AB, 0x04AD, 0x04AF,
	0x04B1, 0x04B3, 0x04B5, 0x04B7, 0x04B9, 0x04BB, 0x04BD, 0x04BF,*/   0x04C2, 0x04C4,
	0x04C8, 0x04CC, /*0x04D1,   0x04D3, 0x04D5, 0x04D7, 0x04D9, 0x04DB, 0x04DD, 0x04DF,
	0x04E1, 0x04E3, 0x04E5, 0x04E7, 0x04E9, 0x04EB, 0x04ED, 0x04EF, 0x04F1, 0x04F3,
	0x04F5, 0x04F9, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568,
	0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, 0x0570, 0x0571, 0x0572,
	0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C,
	0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586,*/
	// cn: added Georgian.  Not in the UnicodeData-3.2.0 spreadsheet, but was added later
	/*
	0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, 0x10d8, 0x10d9,
	0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, 0x10e0, 0x10e1, 0x10e2, 0x10e3,
	0x10e4, 0x10e5, 0x10e6, 0x10e7, 0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed,
	0x10ee, 0x10ef, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5,
	// cn: end Georgian.
	*/
	/*0x1E01,   0x1E03, 0x1E05, 0x1E07, 0x1E09, 0x1E0B, 0x1E0D, 0x1E0F, 0x1E11, 0x1E13,
	0x1E15, 0x1E17, 0x1E19, 0x1E1B, 0x1E1D, 0x1E1F, 0x1E21, 0x1E23, 0x1E25, 0x1E27,
	0x1E29, 0x1E2B, 0x1E2D, 0x1E2F, 0x1E31, 0x1E33, 0x1E35, 0x1E37, 0x1E39, 0x1E3B,
	0x1E3D, 0x1E3F, 0x1E41, 0x1E43, 0x1E45, 0x1E47, 0x1E49, 0x1E4B, 0x1E4D, 0x1E4F,
	0x1E51, 0x1E53, 0x1E55, 0x1E57, 0x1E59, 0x1E5B, 0x1E5D, 0x1E5F, 0x1E61, 0x1E63,
	0x1E65, 0x1E67, 0x1E69, 0x1E6B, 0x1E6D, 0x1E6F, 0x1E71, 0x1E73, 0x1E75, 0x1E77,
	0x1E79, 0x1E7B, 0x1E7D, 0x1E7F, 0x1E81, 0x1E83, 0x1E85, 0x1E87, 0x1E89, 0x1E8B,
	0x1E8D, 0x1E8F, 0x1E91, 0x1E93, 0x1E95, 0x1EA1, 0x1EA3, 0x1EA5, 0x1EA7, 0x1EA9,
	0x1EAB, 0x1EAD, 0x1EAF, 0x1EB1, 0x1EB3, 0x1EB5, 0x1EB7, 0x1EB9, 0x1EBB, 0x1EBD,
	0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1EC9, 0x1ECB, 0x1ECD, 0x1ECF, 0x1ED1,
	0x1ED3, 0x1ED5, 0x1ED7, 0x1ED9, 0x1EDB, 0x1EDD, 0x1EDF, 0x1EE1, 0x1EE3, 0x1EE5,
	0x1EE7, 0x1EE9, 0x1EEB, 0x1EED, 0x1EEF, 0x1EF1, 0x1EF3, 0x1EF5, 0x1EF7, 0x1EF9,
	0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07, 0x1F10, 0x1F11,
	0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25,
	0x1F26, 0x1F27, 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,*/
	0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F51, 0x1F53, 0x1F55, 0x1F57,
	0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67, 0x1F80, 0x1F81,
	0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87, 0x1F90, 0x1F91, 0x1F92, 0x1F93,
	0x1F94, 0x1F95, 0x1F96, 0x1F97, 0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5,
	0x1FA6, 0x1FA7, 0x1FB0, 0x1FB1, 0x1F70, 0x1F71, 0x1FB3, 0x1F72, 0x1F73, 0x1F74,
	0x1F75, 0x1FC3, 0x1FD0, 0x1FD1, 0x1F76, 0x1F77, 0x1FE0, 0x1FE1, 0x1F7A, 0x1F7B,
	0x1FE5, 0x1F78, 0x1F79, 0x1F7C, 0x1F7D, 0x1FF3, 0x03C9, 0x006B, 0x00E5 /*0x2170,
	0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A,
	0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4,
	0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE,
	0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8,
	0x24E9, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49,
	0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53,
	0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A*/
};


// WARNING: This is used by the core flash code. Any change to this utility, or the tables
//          it relies on, will break legacy Flash content.
uint32_t unicharToLower(uint32_t ch)
{
	if (ch < 0xFF)
		return tolower_map[ch] ^ ch;

	// offset x1C60
	if( ch>=0x10A0 && ch<=0x10C5 )  // Georgian
	{
		return (ch + 48); // WRONG
		//return (ch + 0x1C60); // CORRECT
	}

	// offset 80
	if ( (ch>=0x0400 && ch<=0x040F) )
	{
		return (ch + 0x50);
	}

	// offset 48
	if ( (ch>=0x0531 && ch<=0x0556) )
	{
		return (ch + 0x30);
	}

	// offset 32
	if ( (ch>=0x0391 && ch<=0x03AB) ||
		 (ch>=0x0410 && ch<=0x042F) ||
		 (ch>=0xFF21 && ch<=0xFF3A) )
	{
		return (ch + 0x20);
	}

	// offset 26
	if ( (ch>=0x24B6 && ch<=0x24CF) )
	{
		return (ch + 0x1A);
	}

	// offset 16
	if ( (ch>=0x2160 && ch<=0x216F) )
	{
		return (ch + 0x10);
	}

	// offset -8(negative)
	if ( (ch>=0x1F08 && ch<=0X1F0F) ||
		 (ch>=0x1F18 && ch<=0x1F1D) ||
		 (ch>=0x1F28 && ch<=0x1F2F) ||
		 (ch>=0x1F38 && ch<=0x1F3F) )
	{
		return (ch - 0x8);
	}

	// offset +1
	if ( (ch>=0x0100 && ch<=0x0232) )
	{
		// only even numbers
		if ( ((ch<=0x012E) && !(ch&0x1)) ||
			 ((ch>=0x0139 && ch<=0x0147) && (ch&0x1)) ||
			 ((ch>=0x014A && ch<=0x0176) && !(ch&1)) ||
			 ((ch>=0x0200 && ch<=0x0232) && !(ch&1) && (ch!=0x0220)) )
		{
			return (ch + 1);
		}
	}

	if ( !(ch&1) ) // only looking for even chars
	{
		if ( (ch>=0x03D8 && ch<=0x03EE) )
				return (ch + 1);
		if ( ((ch>=0x0460 && ch<=0x04BE) && !(ch==0x0482 || ch==0x0484 || ch==0x486 || ch==0x0488)) ||
			  (ch>=0x04D0 && ch<=0x04F8) )
		{
				if (ch!=0x0482 && ch!=0x0484 && ch!=0x486 && ch!=0x0488)
					return (ch + 1);
		}
		if ( (ch>=0x1E00 && ch<=0x1E94) || (ch>=0x1EA0 && ch<=0x1EF8) )
			return (ch + 1);
	}


	uint32_t result = ch;
	// Do a binary search in upperCaseBase for char
	int32_t lo = 0;
	int32_t hi = (sizeof(upperCaseBase) / sizeof(upperCaseBase[0])) - 1;

	while (lo <= hi)
	{
		int32_t pivot = (lo+hi)>>1;
		uint32_t testChar = upperCaseBase[pivot];

		if (ch == testChar)
		{
			// Use that index into lowerCaseConversion for a return value
			result =  lowerCaseConversion[pivot];
			break;
		}
		else if (ch < testChar)
		{
			hi = pivot-1;
		}
		else
		{
			lo = pivot+1;
		}
	}

	return result;
}

tiny_string tiny_string::lowercase() const
{
	// have to loop manually, because g_utf8_strdown doesn't
	// handle nul-chars
	tiny_string ret;
	uint32_t allocated = 2*numBytes()+7;
	ret.createBuffer(allocated);
	char *p = ret.buf;
	uint32_t len = 0;
	for (CharIterator it=begin(); it!=end(); it++)
	{
		gunichar c = unicharToLower(*it);
		gint n = g_unichar_to_utf8(c, p);
		p += n;
		len += n;
	}
	*p = '\0';
	ret.stringSize = len+1;
	ret.init();
	return ret;
}

tiny_string tiny_string::uppercase() const
{
	// have to loop manually, because g_utf8_strup doesn't
	// handle nul-chars
	tiny_string ret;
	uint32_t allocated = 2*numBytes()+7;
	ret.createBuffer(allocated);
	char *p = ret.buf;
	uint32_t len = 0;
	for (CharIterator it=begin(); it!=end(); it++)
	{
		gunichar c = unicharToUpper(*it);
		gint n = g_unichar_to_utf8(c, p);
		p += n;
		len += n;
	}
	*p = '\0';
	ret.stringSize = len+1;
	ret.init();
	return ret;
}

/* like strcasecmp(s1.raw_buf(),s2.raw_buf()) but for unicode
 * TODO: slow! */
int tiny_string::strcasecmp(tiny_string& s2) const
{
	char* str1 = g_utf8_casefold(this->raw_buf(),this->numBytes());
	char* str2 = g_utf8_casefold(s2.raw_buf(),s2.numBytes());
	int ret = g_utf8_collate(str1,str2);
	g_free(str1);
	g_free(str2);
	return ret;
}

uint32_t tiny_string::bytePosToIndex(uint32_t bytepos) const
{
	if (bytepos >= numBytes())
		return numChars();
	if (isASCII)
		return bytepos;

	return g_utf8_pointer_to_offset(raw_buf(), raw_buf() + bytepos);
}

CharIterator tiny_string::begin()
{
	return CharIterator(buf);
}

CharIterator tiny_string::begin() const
{
	return CharIterator(buf);
}

CharIterator tiny_string::end()
{
	//points to the trailing '\0' byte
	return CharIterator(buf+numBytes());
}

CharIterator tiny_string::end() const
{
	//points to the trailing '\0' byte
	return CharIterator(buf+numBytes());
}

int tiny_string::compare(const tiny_string& r) const
{
	int l = std::min(stringSize,r.stringSize);
	int res = 0;
	for(int i=0;i < l-1;i++)
	{
		res = (int)buf[i] - (int)r.buf[i];
		if (res != 0)
			return res;
	}
	if (stringSize > r.stringSize)
		res = 1;
	else if (stringSize < r.stringSize)
		res = -1;
	return res;
}

tiny_string tiny_string::toQuotedString() const
{
	tiny_string res("\"");
	for (CharIterator it=this->begin(); it!=this->end(); it++)
	{
		switch (*it)
		{
			case '\b':
				res += "\\b";
				break;
			case '\f':
				res += "\\f";
				break;
			case '\n':
				res += "\\n";
				break;
			case '\r':
				res += "\\r";
				break;
			case '\t':
				res += "\\t";
				break;
			case '\"':
				res += "\\\"";
				break;
			case '\\':
				res += "\\\\";
				break;
			default:
				if ((*it < 0x20) || (*it > 0xff))
				{
					char hexstr[7];
					sprintf(hexstr,"\\u%04x",*it);
					res += hexstr;
				}
				else
					res += *it;
				break;
		}
	}
	res += "\"";
	return res;
}

void tiny_string::getTrimPositions(uint32_t& start, uint32_t& end) const
{
	start = 0;
	end = stringSize-1;
	if (empty())
		return;
	while (start < stringSize)
	{
		uint8_t c = buf[start];
		if (c&0x80) //check for unicode whitspace character
		{
			if (!g_unichar_isspace(g_utf8_get_char(buf+start)))
				break;
			while (c&0x80) // skip utf8 bytes
			{
				c=c<<1;
				start++;
			}
		}
		else if (!isspace(c))
			break;
		else
			start++;
	}
	while (end > start)
	{
		uint8_t c = buf[end-1];
		if (c&0x80) //check for unicode whitspace character
		{
			while (((buf[end])&0xc0) == 0x80) // skip utf8 bytes
			{
				end--;
			}
			if (!g_unichar_isspace(g_utf8_get_char(buf+end)))
				break;
		}
		else if (!isspace(c))
			break;
		else
			end--;
	}
}

tiny_string tiny_string::removeWhitespace() const
{
	uint32_t start,end;
	getTrimPositions(start,end);
	return substr_bytes(start,end-start);
}
bool tiny_string::isWhiteSpaceOnly() const
{
	uint32_t start,end;
	getTrimPositions(start,end);
	return start == end;
}

tiny_string tiny_string::encodeNull() const
{
	if (!this->hasNull)
		return *this;
	tiny_string res;
	auto it = this->begin();
	while (it != this->end())
	{
		switch (*it)
		{
			case '\0':
				res += "&#x0;";
				break;
			default:
				res += *it;
				break;
		}
		it++;
	}
	return res;
}

#ifdef MEMORY_USAGE_PROFILING
void tiny_string::reportMemoryChange(int32_t change) const
{
	getSys()->stringMemory->addBytes(change);
}
#endif
